using System;
using System.Xml;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Editor
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {

        #region Editor Variables
        String levelName = "Maps\\Level4";
        Texture2D mapTexture;

        Vector2 mouseScreen;
        Vector2 pressPoint = Vector2.Zero;
        Vector2 releasePoint = Vector2.Zero;
        MouseState lastMouseState;
        Texture2D mouseTexture;

        KeyboardState lastKeyboardState;

        Vector2 Camera = Vector2.Zero;
        #endregion

        #region Field Cell Variables
        
        Texture2D cellTexture;
        byte[,] cell;
        int cellSize = 15;
        int mapCellWidth;
        int mapCellHeight;
        #endregion

        #region Map Object Variables
        //Map Object variables.
        String mapObjStr = "";  //String of all map objects.
        const int OBJECT_NUMBER = 2; //Number of different map objects.
        bool itemType;   //false for a grid property(such as an invisible block), true for a map object(such as a tree)
        int itemSelected = 1;//ID number of item selected
        Texture2D[] objTexture = new Texture2D[OBJECT_NUMBER];//Map Object Textures
        #endregion

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        MapArea map;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            mapTexture = Content.Load<Texture2D>(levelName);
            cellTexture = Content.Load<Texture2D>("Cell2");
            mouseTexture = Content.Load<Texture2D>("Mouse");
            Dictionary<MapObject.ObjectType, Texture2D> objectTextures = new Dictionary<MapObject.ObjectType, Texture2D>();
            objectTextures.Add(MapObject.ObjectType.SpawnPoint, Content.Load<Texture2D>("Tanks/tank2full"));
            objectTextures.Add(MapObject.ObjectType.Tree, Content.Load<Texture2D>("MapObjects/Obj1"));

            Dictionary<Type, Texture2D> dicMenuItemTextures = new Dictionary<Type, Texture2D>();
            dicMenuItemTextures.Add(typeof(MinimizeSideMenuBar), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorSideBarStrip"));
            dicMenuItemTextures.Add(typeof(SaveMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorSave"));
            dicMenuItemTextures.Add(typeof(LoadMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorLoad"));
            dicMenuItemTextures.Add(typeof(ClearMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorClear"));
            dicMenuItemTextures.Add(typeof(BlockMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorBlock"));
            dicMenuItemTextures.Add(typeof(WaterMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorWater"));
            dicMenuItemTextures.Add(typeof(HighGroundMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorHighGround"));
            dicMenuItemTextures.Add(typeof(CliffMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorCliff"));
            dicMenuItemTextures.Add(typeof(SpawnMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorSpawn"));
            dicMenuItemTextures.Add(typeof(MapObjectMenuItem), Content.Load<Texture2D>("MapEditorMenuItems/MapEditorMapObject"));

            this.map = new MapArea(this, mapTexture, levelName, cellTexture, mouseTexture, objectTextures, dicMenuItemTextures);

            this.graphics.GraphicsDevice.Viewport = this.map.SideMenu.ResizeViewport(this.graphics.GraphicsDevice.Viewport);

            //Calculate the number of cells for the map.
            mapCellWidth  = mapTexture.Width/cellSize  + 1;
            mapCellHeight = mapTexture.Height/cellSize + 1;
            cell = new byte[mapCellWidth,mapCellHeight];
            for (int x = 0; x < mapCellWidth; x++)
                for (int y = 0; y < mapCellHeight; y++)
                    cell[x, y] = 0;

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here
            if (this.IsActive)
            {
                this.map.getUserInput(Keyboard.GetState(), Mouse.GetState(), this.graphics.GraphicsDevice.Viewport);
            }

            if (this.map.SideMenu.IsMinimizing || this.map.SideMenu.IsMaxamizing)
            {
                this.graphics.GraphicsDevice.Viewport = this.map.SideMenu.ResizeViewport(this.graphics.GraphicsDevice.Viewport);
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            this.map.Draw(spriteBatch, this.graphics.GraphicsDevice);
            //spriteBatch.Begin();

            /*
            //Draw the background map
            spriteBatch.Draw(mapTexture, Vector2.Zero-Camera,Color.White);

            //Draws all the mapObjects
            #region Draw mapObjects
            int objectIndex=0;
            Vector2 objPos = Vector2.Zero;
            int type=0;
            String token="";
            for (int s = 0; s < mapObjStr.Length; s++)
            {
                if (mapObjStr[s] == ' ')
                {
                    if (objectIndex == 0)
                    {
                        type = Int32.Parse(token);
                    }
                    else if (objectIndex == 1)
                    {
                        objPos.X = Int32.Parse(token);
                    }
                    else if (objectIndex == 2)
                    {
                        objPos.Y = Int32.Parse(token);
                    }
                    objectIndex++;
                    token = "";
                }
                else if (mapObjStr[s] == '\n')
                {
                    spriteBatch.Draw(objTexture[type], objPos - Camera, Color.White);
                    objectIndex = 0;
                    token = "";
                }
                else
                {
                    token += mapObjStr[s];
                }
            }//For
            #endregion

            //Draws the grid properties including colliding blocks
            #region Draw Grid Properties
            for (int x = 0; x < mapCellWidth; x++)
            for (int y=0; y<  mapCellHeight;y++)
            {
                if (cell[x,y] > 0)
                {
                    Vector2 cellPosition = new Vector2(x*cellSize,y*cellSize);
                    
                    //Determine the objects color.
                    Color objColor = Color.White;
                    if (cell[x,y] == 1) //Block
                        objColor = new Color(255,0,0,100);
                    else if (cell[x, y] == 2)     //Cliff
                        objColor = new Color(0,255,0,100);
                    else if (cell[x,y] ==3)     //Water
                        objColor = new Color(0,0,255,100);
                    else if   (cell[x,y] ==4)   //High Ground
                        objColor = new Color(255,255,0,100);
                    spriteBatch.Draw(cellTexture, cellPosition - Camera, objColor);
                }
            }
            #endregion

            //Draws all the objects at the mouse.
            #region Draw Mouse Objects

            Vector2 objPosition = new Vector2();
            objPosition.X = mouseScreen.X - (mouseScreen.X % 15);
            objPosition.Y = mouseScreen.Y - (mouseScreen.Y % 15);
            if (itemType)
            {
                spriteBatch.Draw(objTexture[itemSelected], objPosition, Color.White);
            }
            else  //Else draw a grid square.
            {
                //Draws a single square at mouse.
                #region Get Color
                Color objColor = Color.White;
                if (itemSelected == 1) //Block
                    objColor = new Color(255, 0, 0);
                else if (itemSelected == 2)   //Cliff
                    objColor = new Color(0, 255, 0);
                else if (itemSelected == 3)   //Water
                    objColor = new Color(0, 0, 255);
                else if (itemSelected == 4)   //HighGround
                    objColor = new Color(255, 255, 0);
                #endregion
                spriteBatch.Draw(cellTexture, objPosition, objColor);

                //If a button is held down draw a box of cells.
                #region Draw Box Selection
                if ((lastMouseState.LeftButton == ButtonState.Pressed) || (lastMouseState.RightButton == ButtonState.Pressed))
                {
                    //Convert get presspoint and convert it into map coordiantes.
                    Vector2 boxStart = new Vector2();
                    boxStart.X = ((int)pressPoint.X) * 15;
                    boxStart.Y = ((int)pressPoint.Y) * 15;

                    //Convert from screen coordinates to map coordinates.
                    objPosition = objPosition + Camera;

                    //Find topLeft coordinates, as well as width and height of box.
                    Vector2 boxTopLeft = new Vector2();
                    int boxWidth = 0;
                    int boxHeight = 0;
                    if (boxStart.X < objPosition.X)
                    {
                        boxWidth = (int)(objPosition.X - boxStart.X + 1);
                        boxTopLeft.X = boxStart.X;
                    }
                    else
                    {
                        boxWidth = (int)(boxStart.X - objPosition.X + 1);
                        boxTopLeft.X = objPosition.X;
                    }
                    if (boxStart.Y < objPosition.Y)
                    {
                        boxHeight = (int)(objPosition.Y - boxStart.Y + 1);
                        boxTopLeft.Y = boxStart.Y;
                    }
                    else
                    {
                        boxHeight = (int)(boxStart.Y - objPosition.Y + 1);
                        boxTopLeft.Y = objPosition.Y;
                    }

                    //Draw square at last place pressed.
                    for (int x = 0; x <= boxWidth/15; x++)
                    for (int y = 0; y <= boxHeight/15; y++)
                    {
                        Vector2 drawVector = new Vector2(boxTopLeft.X+x*15,boxTopLeft.Y+y*15);
                        spriteBatch.Draw(cellTexture, drawVector - Camera, objColor);
                    }

                }//If: button was held down.
                #endregion

            }//Else draw grid square

            //Draw the mouse pointer.
            spriteBatch.Draw(mouseTexture, mouseScreen, Color.White);

            #endregion
            */

            //spriteBatch.End();

            base.Draw(gameTime);
        }

        public void getUserInput()
        {
            #region Keyboard Input
            KeyboardState keyboard = Keyboard.GetState();

            if (keyboard.IsKeyDown(Keys.Left))
            {
                Camera.X -= 15;
            }
            if (keyboard.IsKeyDown(Keys.Right))
            {
                Camera.X += 15;
            }
            if (keyboard.IsKeyDown(Keys.Up))
            {
                Camera.Y -= 15;
            }
            if (keyboard.IsKeyDown(Keys.Down))
            {
                Camera.Y += 15;
            }

            else if ((keyboard.IsKeyDown(Keys.S)) && (lastKeyboardState.IsKeyUp(Keys.S)))
            {
                #region Save Map to File

                XmlTextWriter mapWriter = null;
                mapWriter = new XmlTextWriter("Maps\\"+levelName+".xml", null);

                try
                {
                    mapWriter.Formatting = Formatting.Indented;
                    mapWriter.Indentation = 6;
                    mapWriter.Namespaces = false;

                    mapWriter.WriteStartDocument();

                    mapWriter.WriteStartElement("","Level","");//<Level>

                    mapWriter.WriteStartElement("","MapImage","");
                    mapWriter.WriteString("Level3");
                    mapWriter.WriteEndElement();

                    mapWriter.WriteStartElement("","CellSize","");
                    mapWriter.WriteString(cellSize.ToString());
                    mapWriter.WriteEndElement();

                    mapWriter.WriteStartElement("", "Grid", "");
                    mapWriter.WriteString(gridToString());
                    mapWriter.WriteEndElement();

                    mapWriter.WriteStartElement("", "MapObjects", "");
                    mapWriter.WriteString(mapObjStr);
                    mapWriter.WriteEndElement();

                    mapWriter.WriteEndElement();//<\Level>
                    mapWriter.WriteEndDocument();
                    mapWriter.Close();
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception: {0}", e.ToString());
                }
                #endregion
            }
            else if ((keyboard.IsKeyDown(Keys.L)) && (lastKeyboardState.IsKeyUp(Keys.L)))
            {
                #region Load Map from File
                XmlTextReader mapReader = null;
                mapReader = new XmlTextReader("Maps\\" + levelName + ".xml");

                while (mapReader.Read())
                {
                    if (mapReader.NodeType == XmlNodeType.Element)
                    {
                        if (mapReader.LocalName.Equals("MapObjects"))
                        {
                            mapObjStr = mapReader.ReadString();
                        }
                        if (mapReader.LocalName.Equals("Grid"))
                        {
                            //Get the grid string
                            String gridString = mapReader.ReadString();


                            //Converts the grid string into a matrix of integers.
                            int x = 0;
                            int y = -1;
                            for (int s = 0; s < gridString.Length; s++)
                            {
                                if (gridString[s] == ' ')
                                {
                                    x++;
                                }
                                else if (gridString[s] == '\n')
                                {
                                    x = 0;
                                    y++;
                                }
                                else if (gridString[s] == ';')
                                {
                                    break;
                                }
                                else
                                {
                                   cell[x,y] = (byte)Int32.Parse(gridString[s] + "");
                                }
                            }

                        }//If: Grid
                    }
                }//While: read
                mapReader.Close();
                #endregion
            }
            else if ((keyboard.IsKeyDown(Keys.Tab))&&(lastKeyboardState.IsKeyUp(Keys.Tab)))
            {
                itemType = (!itemType);
                if (itemType)
                    itemSelected = 0;
                else
                    itemSelected = 1;
            }
            else if ((keyboard.IsKeyDown(Keys.OemPlus)) && (lastKeyboardState.IsKeyUp(Keys.OemPlus)))
            {
                itemSelected++;
                if (itemType)
                {
                    if (itemSelected >= OBJECT_NUMBER)
                        itemSelected =0;
                }
                else
                if (itemSelected > 4)
                    itemSelected = 1;
            }
            else if ((keyboard.IsKeyDown(Keys.OemMinus)) && (lastKeyboardState.IsKeyUp(Keys.OemMinus)))
            {
                itemSelected--;
                if (itemType)
                {
                    if (itemSelected < 0)
                        itemSelected = OBJECT_NUMBER-1;
                }
                else
                if (itemSelected < 1)
                    itemSelected = 4;
            }

            lastKeyboardState = keyboard;
            #endregion

            #region MouseInput
            MouseState mouseState = Mouse.GetState();

            mouseScreen.X = mouseState.X;
            mouseScreen.Y = mouseState.Y;

            Vector2 mouseMap;
            mouseMap.X = mouseScreen.X + Camera.X;
            mouseMap.Y = mouseScreen.Y + Camera.Y;

            #region Left Click
            if ((mouseState.LeftButton == ButtonState.Pressed)&&(lastMouseState.LeftButton == ButtonState.Released))
            {
                //Get press point in cell coordinates.
                pressPoint = mouseMap/15;

                if (itemType)
                {
                    pressPoint.X = (int)(pressPoint.X) * 15;
                    pressPoint.Y = (int)(pressPoint.Y) * 15;
                    mapObjStr += itemSelected.ToString() + " ";
                    mapObjStr += pressPoint.X.ToString() + " ";
                    mapObjStr += pressPoint.Y.ToString() + " \n";
                }
            }
            else if ((mouseState.LeftButton == ButtonState.Released)&&(lastMouseState.LeftButton == ButtonState.Pressed))
            {
                if (itemType)
                {
                    lastMouseState = mouseState;
                    return;
                }

                //Find release point in cell coordinates.
                releasePoint = mouseMap/15;
                
                //Get square topLeft.
                Vector2 squareTopLeft;
                if (pressPoint.X < releasePoint.X)
                    squareTopLeft.X = (int)pressPoint.X;
                else
                    squareTopLeft.X = (int)releasePoint.X;

                if (pressPoint.Y < releasePoint.Y)
                    squareTopLeft.Y = (int)pressPoint.Y;
                else
                    squareTopLeft.Y = (int)releasePoint.Y;

                //Get square width and height in cells.
                int squareWidth =  ((int)pressPoint.X - (int)releasePoint.X);
                int squareHeight = ((int)pressPoint.Y - (int)releasePoint.Y);
                if (squareWidth < 0)
                    squareWidth *= (-1);
                if (squareHeight < 0)
                    squareHeight *= -(1);

                for (int x = (int)squareTopLeft.X; x <= squareTopLeft.X + squareWidth; x++)
                for (int y = (int)squareTopLeft.Y; y <= squareTopLeft.Y + squareHeight; y++)
                {
                    if ((x < 0) || (x >= mapCellWidth))
                        break;
                    if ((y < 0) || (y >= mapCellHeight))
                        break;

                    cell[x, y] = (byte)itemSelected;
                }
            }//Else if button is released
            #endregion

            #region Right Click
            if ((mouseState.RightButton == ButtonState.Pressed) && (lastMouseState.RightButton == ButtonState.Released))
            {
                //Get press point in cell coordinates.
                pressPoint = mouseMap / 15;
            }
            else if ((mouseState.RightButton == ButtonState.Released) && (lastMouseState.RightButton == ButtonState.Pressed))
            {
                if (itemType)
                {
                    lastMouseState = mouseState;
                    return;
                }

                //Find release point in cell coordinates.
                releasePoint = mouseMap / 15;

                //Get square topLeft.
                Vector2 squareTopLeft;
                if (pressPoint.X < releasePoint.X)
                    squareTopLeft.X = (int)pressPoint.X;
                else
                    squareTopLeft.X = (int)releasePoint.X;

                if (pressPoint.Y < releasePoint.Y)
                    squareTopLeft.Y = (int)pressPoint.Y;
                else
                    squareTopLeft.Y = (int)releasePoint.Y;

                //Get square width and height in cells.
                int squareWidth = ((int)pressPoint.X - (int)releasePoint.X);
                int squareHeight = ((int)pressPoint.Y - (int)releasePoint.Y);
                if (squareWidth < 0)
                    squareWidth *= (-1);
                if (squareHeight < 0)
                    squareHeight *= -(1);

                for (int x = (int)squareTopLeft.X; x <= squareTopLeft.X + squareWidth; x++)
                for (int y = (int)squareTopLeft.Y; y <= squareTopLeft.Y + squareHeight; y++)
                    {
                        if ((x < 0) || (x >= mapCellWidth))
                            break;
                        if ((y < 0) || (y >= mapCellHeight))
                            break;

                        cell[x, y] = 0;
                    }
            }//Else if button is released
            #endregion
            lastMouseState = mouseState;
            #endregion
        }//Method get user Input

        public String gridToString()
        {
            String str="\n";
           
            for (int y = 0; y < mapCellHeight; y++)
            {
                for (int x = 0; x < mapCellWidth; x++)
                {
                    str += cell[x, y].ToString();
                    str += " ";
                }
                str += "\n";
            }
            str += ";";

            return str;
        }
    }//End of class
}//End of namespace
